package cn.chendd.sshd.utils;

import lombok.Builder;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClientFactory;
import org.apache.sshd.sftp.common.SftpHelper;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 操作 Sftp 工具类
 *
 * @author chendd
 * @date 2023/11/18 18:06
 */
public final class SftpUtils {

    /**
     * 路径分隔符
     */
    private static final String SEPARATOR = "/";

    private SshClient sshClient;
    private ClientSession session;
    private SftpClient sftpClient;

    public static SftpUtils newInstance(SftpParam param) {
        final SftpUtils sftp = new SftpUtils();
        try {
            sftp.connect(param);
        } catch (IOException e) {
            if (sftp.sshClient != null) {
                try {
                    sftp.sshClient.close();
                } catch (IOException ignore) {}
            }
            if (sftp.session != null) {
                try {
                    sftp.session.close();
                } catch (IOException ignore) {}
            }
            throw new RuntimeException(e);
        }
        return sftp;
    }

    public void connect(SftpParam param) throws IOException {
        if (sftpClient != null) {
            sftpClient.close();
        }
        if (session != null && session.isAuthenticated()) {
            session.close();
        }
        if (sshClient == null || !sshClient.isOpen()) {
            sshClient = SshClient.setUpDefaultClient();

            sshClient.start();
        }
        session = sshClient.connect(param.getUsername(), param.getHost(), param.getPort()).verify().getSession();
        session.addPasswordIdentity(param.getPassword());
        session.auth().verify();
        sftpClient = SftpClientFactory.instance().createSftpClient(session);
    }

    /**
     * 上传文件
     * @param srcFile 源文件
     * @param dest 目标文件
     * @throws IOException 异常处理
     */
    public void uploadFile(File srcFile , String dest) throws IOException {
        try (OutputStream outputStream = this.sftpClient.write(dest)) {
            Files.copy(srcFile.toPath(), outputStream);
        }
    }

    /**
     * 下载文件
     * @param src 源文件
     * @param destFile 目标文件
     * @throws IOException 异常处理
     */
    public void downloadFile(String src , File destFile) throws IOException {
        try (InputStream inputStream = this.sftpClient.read(src)) {
            Files.copy(inputStream , destFile.toPath() , StandardCopyOption.REPLACE_EXISTING);
        }
    }

    /**
     * 根据文件夹路径获取文件列表
     * @param path 路径
     * @return 数据对象
     * @throws IOException 异常处理
     */
    public List<FileEntry> listFiles(String path) throws IOException {
        final Iterable<SftpClient.DirEntry> iterable = this.sftpClient.readDir(path);
        List<String> ignore = Arrays.asList(".", "..");
        List<FileEntry> list = new ArrayList<>();
        SftpClient.DirEntry item;
        for (SftpClient.DirEntry entry : iterable) {
            final String name = entry.getFilename();
            if (ignore.contains(name)) {
                continue;
            }
            final SftpClient.Attributes attr = entry.getAttributes();
            list.add(FileEntry.builder()
                    .name(name).size(attr.getSize()).updateTime(new Date(attr.getModifyTime().toMillis()))
                    .permission(PosixFilePermissions.toString(SftpHelper.permissionsToAttributes(attr.getPermissions())))
                    .dir(attr.isDirectory()).longname(entry.getLongFilename())
                    .build());
        }
        return list;
    }
    /**
     * 重命名文件（夹）
     * @param oldPath 旧文件（夹）
     * @param newPath 新文件（夹）
     * @throws IOException 异常处理
     */
    public void renameFile(String oldPath , String newPath) throws IOException {
        this.sftpClient.rename(oldPath , newPath);
    }

    /**
     * 删除文件
     * @param path 文件路径
     * @throws IOException 异常处理
     */
    public void removeFile(String path) throws IOException {
        this.sftpClient.remove(path);
    }

    /**
     * 删除文件夹【要求删除的文件夹必须为空文件夹】
     * @param path 文件夹路径
     * @throws IOException 异常处理
     */
    public void removeFolder(String path) throws IOException {
        this.sftpClient.rmdir(path);
    }

    /**
     * 判断路径是否为文件夹
     * @param path 路径
     * @return true ？ 文件夹 ：文件
     */
    public boolean isDir(String path) {
        try {
            SftpClient.Attributes attrs = this.sftpClient.stat(path);
            return attrs.isDirectory();
        } catch (Exception ignore){
            return false;
        }
    }

    /**
     * 路径是否存在【约定文件夹以‘/’结尾】
     * @param path 路径
     * @return true ? 存在 ：不存在
     */
    public boolean exists(String path) {
        try {
            SftpClient.Attributes attrs = this.sftpClient.stat(path);
            final boolean dir = attrs.isDirectory();
            if (StringUtils.endsWith(path , SEPARATOR)) {
                return dir;
            } else {
                return !dir;
            }
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 创建文件夹路径
     * @param path 文件夹路径
     * @throws IOException 异常处理
     */
    public void mkdirs(String path) throws IOException {
        if (! StringUtils.endsWith(path , SEPARATOR)) {
            path = path + SEPARATOR;
        }
        if (this.isDir(path)) {
            return;
        }
        String[] paths = StringUtils.split(path , SEPARATOR);
        StringBuilder pathBuilder = new StringBuilder(SEPARATOR);
        String temp;
        for (String p : paths) {
            if (StringUtils.isEmpty(p)) {
                continue;
            }
            pathBuilder.append(p).append(SEPARATOR);
            temp = pathBuilder.toString();
            if (this.exists(temp)) {
                continue;
            }
            this.sftpClient.mkdir(temp);
        }
    }

    public void disconnect() {
        if (sftpClient != null) {
            try {
                sftpClient.close();
            } catch (IOException ignore) {}
        }
        if (session != null) {
            try {
                session.close();
            } catch (IOException ignore) {}
        }
        if (sshClient != null && !sshClient.isClosed()) {
            try {
                sshClient.close();
            } catch (IOException ignore) {}
            sshClient.stop();

        }
    }

    /**
     * 文件对象
     */
    @Data
    @Builder
    public static class FileEntry {

        /**
         * 文件（夹）名称
         */
        private String name;
        /**
         * 文件大小
         */
        private long size;
        /**
         * 更新时间
         */
        private Date updateTime;
        /**
         * 权限
         */
        private String permission;
        /**
         * 是否为文件夹
         */
        private boolean dir;
        /**
         * 文件或目录的详细信息，包括权限、所有者、所属组、大小、修改时间等
         */
        private String longname;

    }

}
